/**
 * 控件拖拽数据传输
 */
import { WidgetData, WidgetMode } from '@/types'
import { forceReflow } from '@/utils'

/** 控件拖拽数据 */
let transfer: WidgetData | null = null

/** 拖拽结束时清除拖拽数据 */
if (typeof document !== 'undefined') {
  document.addEventListener('dragend', () => (transfer = null))
}

/**
 * 设置拖拽的控件数据（拖拽开始）
 * @description 控件默认为设置模式，如果是拖动加盖印章，需要设置控件数据 `mode: WidgetMode.USE`
 * @param {event} e drag 事件
 * @param {WidgetData} data 控件数据
 */
export function setWidgetTransfer(e: DragEvent, data: WidgetData) {
  const target = e.target as HTMLElement | null

  // 如果拖拽元素未启用 draggable，则不允许拖拽
  if (!target?.draggable) return

  transfer = {
    // 默认为设置模式
    mode: data.mode ?? WidgetMode.SET,

    ...data,

    dragOffset: {
      x: e.offsetX - target.clientWidth / 2,
      y: e.offsetY - target.clientHeight / 2
    }
  }

  /**
   * 修复 Chrome v106.0.5249.91 拖拽后页面其他节点空白
   *
   * 拖拽元素时，页面样式和节点都未变化，但是浏览器未能绘制拖拽元素以外的节点。
   *
   * 解决方案：
   * 拖拽事件中更改透明度并迅速还原，强制触发浏览器重绘来解决空白
   *
   * 重现代码：
   *
   *  <div class="container">
   *    <div class="child">Will disappear</div>
   *    <div draggable="true">Drag me</div>
   *  </div>
   *
   *  <style>
   *    .container {
   *      position: absolute;
   *      width: 200px;
   *      height: 100px;
   *    }
   *
   *    .child {
   *      position: relative;
   *      width: 100px;
   *      height: 100%;
   *      background: red;
   *      color: #fff;
   *    }
   *  </style>
   */
  forceReflow(document.body)
}

/**
 * 获取拖拽的控件数据（拖入）
 * @param {DragEvent} e drop 事件
 * @param {Element} area 拖拽区域节点
 * @param {number} zoom 缩放级别
 * @returns {WidgetData} 控件数据
 */
export function getWidgetTransfer(e: DragEvent, area: Element, zoom = 1) {
  /** 阻止火狐将数据新页签打开 */
  e.preventDefault()
  e.stopPropagation()

  const data = transfer

  transfer = null

  if (!data) return null

  const { dragOffset = { x: 0, y: 0 } } = data
  const { left, top } = area.getBoundingClientRect()

  /** 定位 */
  Object.assign(data, {
    ox: Math.floor((e.pageX - left) / zoom - dragOffset.x),
    oy: Math.floor((e.pageY - top) / zoom - dragOffset.y)
  })

  delete data.dragOffset

  return data
}
